package com.jt.www.common.service.impl;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.jt.www.common.service.FileService;
import com.jt.www.common.service.JunZiQianService;
import com.jt.www.common.service.zqsign.dependency.HttpClientUtil;
import com.jt.www.common.service.zqsign.param.agreement.Param;
import com.jt.www.dao.mapper.zq.ZqContractMapper;
import com.jt.www.domain.enums.common.BusinessTypeCodeEnum;
import com.jt.www.exception.ParamException;
import com.jt.www.exception.ServiceException;
import com.jt.www.model.reps.GenericDataResponse;
import com.jt.www.model.zq.ZqContract;
import com.jt.www.remote.FilesNewClient;
import com.jt.www.remote.IdgClient;
import com.jt.www.util.CommonUtils;
import com.jt.www.util.HmacSHA1Util;
import com.jt.www.util.Html2PdfUtil;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.WebContext;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.util.*;
import java.util.regex.Pattern;

/**
 * @描述： 君子签（易保全）服务
 * @作者： lq
 * @创建日期： 2020-9-28 11:11
 * @版权： 江泰保险经纪股份有限公司
 */
@Component
public class JunZiQianServiceImpl implements JunZiQianService {

    private final static String CONTRACT_NAME = "太原学平委托协议";
    private final Logger log = LoggerFactory.getLogger(this.getClass());
    @Value("${jtpf.junziqian.appkey}")
    public String appkey = "";
    @Value("${jtpf.junziqian.secrect}")
    public String secrect = "";
    @Value("${jtpf.junziqian.url}")
    public String url = "";
    @Value("${jtpf.junziqian.returnUrl}")
    public String RETURN_URL = "";
    @Value("${jtpf.junziqian.resDownUrl}")
    public String RES_DOWN_URL = "";
    @Autowired
    public ZqContractMapper zqContractMapper;
    @Autowired
    public FilesNewClient filesNewClient;
    @Autowired
    public FileService fileService;
    @Autowired
    private IdgClient idgClient;
    @Resource
    private TemplateEngine templateEngine;

    /**
     * 共用
     *
     * @return
     */
    public Map<String, String> common() {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
        Long time = System.currentTimeMillis();
        Map<String, String> map = new HashMap<>();
        String sign = HmacSHA1Util.string2Sha1(appkey + time + secrect);
        map.put("appkey", appkey);
        map.put("timestamp", time + "");
        map.put("sign", sign);
        return map;
    }

    /**
     * ping
     *
     * @return
     */
    public ResponseEntity<?> ping() {

        Map<String, String> map = common();
        map.put("method", "ping");
        log.info(JSONObject.toJSON(map).toString());
        String forEntity = HttpClientUtil.doPost(url, map);
        log.info("返回值=" + forEntity);
        return GenericDataResponse.okWithData(forEntity);
    }

    @Override
    @Transactional(rollbackFor = Throwable.class)
    public ResponseEntity<?> saveAgreement(Param agreement, HttpServletRequest request, HttpServletResponse response) throws Exception {

        String findCode = "saveAgreement_" + System.currentTimeMillis();

        //手机号正则表达式
        String regex = "^[1](([3|5|8][\\d])|([4][4,5,6,7,8,9])|([6][2,5,6,7])|([7][^9])|([9][1,8,9]))[\\d]{8}$";
        if (!CommonUtils.isMobiPhoneNum(regex, agreement.getPhoneNum())) {
            throw new ParamException("手机号不合法");
        }
        if (StringUtils.isEmpty(agreement.getCustName())) {
            throw new ParamException("姓名不能为空");
        }
        if (!isIDCard(agreement.getCardNo())) {
            throw new ParamException("身份账号不合法");
        }
        if (getAgeByIDCard(agreement.getCardNo()) < 18) {
            throw new ParamException("您需要填写投保人（家长）的姓名、身份证号，您当前填写的投保人不满18周岁，请修改");
        }
        ZqContract zqContract = agreement.getZqContract();

        //合同编码
        String conAgreementNo = String.valueOf(idgClient.nextId());
        zqContract.setContractNo(conAgreementNo);
        zqContract.setContractName(CONTRACT_NAME);
        zqContract.setProductCode("19PR999676");
        zqContract.setProductName("太原市学平乐专属保险");

        log.info("{} 生成协议入参 {}", findCode, zqContract);

        String fileId = createAgreement(findCode, zqContract, request, response);
        zqContract.setCode(fileId);

        log.info("{} 生成协议 文件fileId {}", findCode, fileId);

        //新增协议数据
        ZqContract zq = zqContractMapper.selectByUniquely(zqContract.getIdCardNo(), zqContract.getUserName(), zqContract.getBranchCode(), zqContract.getProductCode());
        Date date = new Date();
        if (zq != null) {
            zqContract.setId(zq.getId());
            zqContract.setUpdateTime(date);
            int i = zqContractMapper.updateByPrimaryKeySelective(zqContract);
            if (i < 1) {
                log.info("{} 生成协议 委托人信息更新失败", findCode);
                throw new ServiceException("委托人信息更新失败！");
            }
        } else {
            zqContract.setCreateTime(date);
            int i = zqContractMapper.insert(zqContract);
            if (i < 1) {
                log.info("{} 生成协议 委托人信息保存失败", findCode);
                throw new ServiceException("委托人信息保存失败！");
            }
        }
        ResponseEntity result = preparedPreviewer(findCode, conAgreementNo);
        log.info("{} 生成协议 JunZiQian result->{}", findCode, result);
        if (result != null && result.getBody() != null) {
            GenericDataResponse resp = ((GenericDataResponse) result.getBody());
            if ("0000".equals(resp.getCode())) {
                return GenericDataResponse.okWithData(resp.getData());
            } else {
                return GenericDataResponse.ng(resp.getMsg());
            }
        } else {
            return GenericDataResponse.ng("调用君子签失败，请刷新后重试！");
        }
    }


    private String createAgreement(String findCode, ZqContract zqContract, HttpServletRequest request, HttpServletResponse response) {

        try {

            WebContext ctx = new WebContext(request, response, request.getServletContext(), request.getLocale());

            LocalDate today = LocalDate.now();

            ctx.setVariable("curYear", today.getYear());
            ctx.setVariable("curMonth", today.getMonthValue());
            ctx.setVariable("curDay", today.getDayOfMonth());

            ctx.setVariable("agreementInfo", zqContract);

            String template = "wtxy-sxty";
            String html = getHtmlByPageName(template);
            String htmlcontext = templateEngine.process(template, ctx);
            log.debug("{} 生成协议 完成", findCode);

            byte[] bytes = Html2PdfUtil.convert(htmlcontext);

            String path = zqContract.getContractNo() + ".pdf";
            savePic(new ByteArrayInputStream(bytes), path);

            MultipartFile multipartFile = new MockMultipartFile(path, path, "", bytes);
            log.info("{} 生成协议 multipartFile信息：{}", findCode, multipartFile);
            MultipartFile[] files = new MultipartFile[]{multipartFile};

            log.info("{} 生成协议 上传文件数组的大小：{}", findCode, files.length);
            String uploadResponse = filesNewClient.batchuploadV3(files, "edu-svc", BusinessTypeCodeEnum.X001.getCode(), "0");
            log.info("{} 生成协议 上传文件的返回值：{}", findCode, uploadResponse);
            JSONObject obj = JSONObject.parseObject(uploadResponse);
            String rc = obj.getString("code");
            String fileId = "";
            if (!"0000".equals(rc)) {
                String msg = obj.getString("msg");
                throw new ServiceException(msg);
            } else {
                JSONArray list = obj.getJSONArray("list");
                for (int i = 0; i < list.size(); i++) {
                    JSONObject fileList = list.getJSONObject(i);
                    fileId = fileList.getString("fileId");
                }
                log.info("{} 生成协议 附件返回的id：{}", findCode, fileId);
            }

            return fileId;
        } catch (Exception e) {
            e.printStackTrace();
            log.info("{} 生成协议异常 {}", findCode, e);
        }
        return null;
    }

    /**
     * 委托协议合同预览
     *
     * @param agreementNo
     * @return
     */
    public ResponseEntity preparedPreviewer(String findCode, String agreementNo) {

        log.info("{} 调用君子签预览合同开始 入参 {}", findCode, agreementNo);
        Map<String, String> map = common();

        ZqContract zqContract = new ZqContract();
        zqContract.setContractNo(agreementNo);
        List<ZqContract> list = zqContractMapper.select(zqContract);
        if (list.isEmpty()) {
            throw new ServiceException("未找到该协议信息");
        }

        zqContract = list.get(0);
        map.put("method", "prepared.previewer");
        map.put("pdfUrl", RES_DOWN_URL + zqContract.getCode());
        map.put("returnUrl", RETURN_URL);
        map.put("type", "1");
        //长度15、18  君子签系统根据身份证校验规则进行验证
        map.put("identityCard", zqContract.getContractNo());
        log.info(JSONObject.toJSON(map).toString());
        String rs = HttpClientUtil.doPost(url, map);
        log.info("{} 调用君子签预览合同 返回值 {}", findCode, rs);
        if (StringUtils.isEmpty(rs)) {
            return GenericDataResponse.ng("调用失败");
        }
        JSONObject json = JSONObject.parseObject(rs);
        if (!json.getBoolean("success")) {
            return GenericDataResponse.ng(json.getString("msg"));
        } else {
            return GenericDataResponse.okWithData(json.getJSONObject("data").getString("url"));
        }
    }

    /**
     * 本地签
     *
     * @return
     */
    public Map<String, String> localSign(Map<String, String> signatory, String fileId) {

        ArrayList<Map<String, String>> armap = new ArrayList<>();
        armap.add(signatory);

        Map<String, String> map = common();
        map.put("method", "local.sign");
        map.put("signatories", JSONObject.toJSON(armap).toString());
        //文件获取类型0直接上传;1通过地址下载;2本地文件直接获取; 默认为0
        map.put("fileType", "1");
        map.put("fileData", RES_DOWN_URL + fileId);
        //0-http直接返回文件;1-返回json结构;2-保存到某个文件夹(其路径为启动参数中配置)内,并返回文件路径;默认为0
        map.put("returnType", "1");
        //签完后是否直接保全:0不保全;1保全
        map.put("signApplyHashFlag", "1");
        map.put("contractName", CONTRACT_NAME);
        log.info(JSONObject.toJSON(map).toString());
        String rs = HttpClientUtil.doPost(url, map);
        log.info("返回值=" + rs);
        String applyNo = "";
        if (StringUtils.isEmpty(rs)) {
            throw new ServiceException("调用失败");
        }

        JSONObject json = JSONObject.parseObject(rs);
        if (!json.getBoolean("success")) {
            throw new ServiceException(json.getString("msg") + json.getString("resultCode"));
        } else {
            byte[] bytes = json.getJSONObject("data").getBytes("signData");
            applyNo = json.getJSONObject("data").getString("applyNo");
            MultipartFile multipartFile = new MockMultipartFile(applyNo + ".pdf", applyNo + ".pdf", "", bytes);
            log.info("multipartFile信息：" + multipartFile);
            MultipartFile[] files = new MultipartFile[]{multipartFile};

            log.info("---------------------上传文件数组的大小：" + files.length);
            String uploadResponse = filesNewClient.batchuploadV3(files, "edu-svc", BusinessTypeCodeEnum.X001.getCode(), "0");
            log.info("----->>上传文件的返回值：{}", uploadResponse);
            JSONObject obj = JSONObject.parseObject(uploadResponse);
            String rc = obj.getString("code");
            if (!"0000".equals(rc)) {
                String msg = obj.getString("msg");
                throw new ServiceException(msg);
            } else {
                JSONArray list = obj.getJSONArray("list");
                for (int i = 0; i < list.size(); i++) {
                    JSONObject fileList = list.getJSONObject(i);
                    fileId = fileList.getString("fileId");
                }
                log.info("附件返回的id：" + fileId);

                Map<String, String> result = new HashMap<>();
                result.put("fileId", fileId);
                result.put("fileName", applyNo + ".pdf");
                return result;
            }
        }
    }

    /**
     * 回调
     *
     * @param method
     * @param timestamp
     * @param sign
     * @param appkey
     * @param data
     * @return
     */
    @Override
    public String affirmBack(String method, Long timestamp, String sign, String appkey, String data) {

        String findCode = "affirmBack_" + System.currentTimeMillis();

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
        Long time = timestamp;
        Map<String, Object> map = new HashMap<>();
        String signs = HmacSHA1Util.string2Sha1(appkey + time + secrect);
        log.info("{} secrect={}", findCode, secrect);
        log.info("{} signs={}", findCode, signs);
        log.info("{} sign={}", findCode, sign);
        if (signs.toUpperCase().equals(sign)) {
            JSONObject jsonObject = JSONObject.parseObject(data);
            log.info("{} jsonObject={}", findCode, jsonObject);
            String identityCard = JSONObject.parseObject(data).getString("identityCard");//存的是协议号
            String handSign = JSONObject.parseObject(data).getString("handSign");//手写签章

            ZqContract zqContract = new ZqContract();
            zqContract.setContractNo(identityCard);
            List<ZqContract> list = zqContractMapper.select(zqContract);
            if (list.isEmpty()) {
                throw new ServiceException("未找到该协议号");
            }
            zqContract = list.get(0);

            //判断证件类型
            String idtype = "1";

            //本地签署合同  盖上个人签署的章
            Map<String, String> signatory = new HashMap<>();
            signatory.put("identityType", idtype);

            signatory.put("fullName", zqContract.getUserName());
            signatory.put("identityCard", identityCard);
            signatory.put("mobile", zqContract.getMobile());
            signatory.put("searchKey", "甲方（委托人）签字：");
            signatory.put("searchConvertExtend", "{\"fixX\":120,\"fixY\":-10}");
            signatory.put("signImg", handSign);

            Map<String, String> result = localSign(signatory, zqContract.getCode());
            String fileId = result.get("fileId");
            String fileName = result.get("fileName");

            Date date = new Date();
            ZqContract updZqContract = new ZqContract();
            updZqContract.setId(zqContract.getId());
            updZqContract.setCode(fileId);
            updZqContract.setIsEfficacy("1");
            updZqContract.setIsSign("1");
            updZqContract.setContractUrl(fileService.getFileDownUrl(fileId, BusinessTypeCodeEnum.X001.getCode(), BusinessTypeCodeEnum.X001.getCode(), "0"));
            updZqContract.setSignTime(date);
            updZqContract.setUpdateTime(date);
            int i = zqContractMapper.updateByPrimaryKeySelective(updZqContract);
            if (i < 1) {
                log.info("{} 异步回调接口更新协议信息失败", findCode);
                throw new ServiceException("异步回调接口更新协议信息失败");
            }
            log.info("{} 异步回调接口更新协议信息成功 updZqContract: {}", findCode, updZqContract);

            map.put("success", true);
            map.put("msg", "");
            return map.toString();
        } else {
            map.put("success", false);
            map.put("msg", "验签失败");
            return map.toString();
        }
    }


    private boolean isIDCard(String idCard) {
        String REGEX_ID_CARD = "(^[1-9]\\d{5}(18|19|([23]\\d))\\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\\d{3}[0-9Xx]$)";
        return Pattern.matches(REGEX_ID_CARD, idCard);
    }

    private int getAgeByIDCard(String idCard) throws Exception {
        String birthday = "";
        if (idCard.length() == 18) {
            birthday = idCard.substring(6, 10) + "/"
                    + idCard.substring(10, 12) + "/"
                    + idCard.substring(12, 14);
        }
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
        Date now = new Date();
        Date birth = sdf.parse(birthday);
        long intervalMilli = now.getTime() - birth.getTime();
        return (int) (intervalMilli / (24 * 60 * 60 * 1000)) / 365;
    }

    private String getHtmlByPageName(String pageName) throws IOException {
        String path = "/templates/" + pageName + ".html";
        // 返回读取指定资源的输入流
        InputStream is = this.getClass().getResourceAsStream(path);
        BufferedReader br = new BufferedReader(new InputStreamReader(is));
        String s = "";
        StringBuffer sb = new StringBuffer();
        while ((s = br.readLine()) != null) {
            sb.append(s).append("\n");
        }
        return sb.toString();

    }

    private void savePic(InputStream inputStream, String fileName) {

        OutputStream os = null;
        try {
            String path = "D:\\save\\";
            // 2、保存到临时文件
            // 1K的数据缓冲
            byte[] bs = new byte[1024];
            // 读取到的数据长度
            int len;
            // 输出的文件流保存到本地文件

            File tempFile = new File(path);
            if (!tempFile.exists()) {
                tempFile.mkdirs();
            }
            os = new FileOutputStream(tempFile.getPath() + File.separator + fileName);
            // 开始读取
            while ((len = inputStream.read(bs)) != -1) {
                os.write(bs, 0, len);
            }

        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 完毕，关闭所有链接
            try {
                os.close();
                inputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
